﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Collections;
using System.IO;
using System.Data;
using System.Threading.Tasks;

namespace ProXMLParser
{
    public class XMLParser : IXMLParser, IXMLParserAsync
    {
        #region Constructor
        public XMLParser()
        {
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Converts XML string to Data Set
        /// </summary>
        /// <param name="XML">XML as string</param>
        /// <returns>Data Set containing the XML string on success, throws Exception on error</returns>                
        public DataSet ConvertXMLToDataSet(string XML)
        {
            DataSet ds = new DataSet();

            try
            {
                XML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + XML;

                System.IO.StringReader sr = new System.IO.StringReader(XML);

                ds.ReadXml(sr);

                return ds;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Converts XML string to Data Set
        /// </summary>
        /// <param name="XML">XML as string</param>
        /// <returns>Data Set containing the XML string on success, throws Exception on error</returns>                
        public DataSet ConvertXMLToDataSet(string XML, bool AppendXMLHeader)
        {
            DataSet ds = new DataSet();
            System.IO.StringReader sr = null;

            try
            {
                if (AppendXMLHeader == false)
                {
                    // Do nothing
                }
                else
                {
                    XML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + XML;
                }

                sr = new System.IO.StringReader(XML);

                ds.ReadXml(sr);

                return ds;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Converts Data Set to XML
        /// </summary>
        /// <param name="ds">Data Set</param>
        /// <returns>XML on success, throws Exception on error</returns>
        public string ConvertDataSetToXML(System.Data.DataSet ds)
        {
            System.IO.StringWriter sw = new System.IO.StringWriter();
            string Parsed_XML = "";

            try
            {
                ds.WriteXml(sw);

                Parsed_XML = sw.ToString();

                return Parsed_XML;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Parses a single node from a XML string
        /// </summary>
        /// <param name="XML">XML</param>
        /// <param name="XPath">Xpath</param>
        /// <returns>String value on success, throws exception on error</returns>
        public string ParseXMLFromString(string XML, string XPath)
        {
            // XML Document
            XmlDocument Xml_Doc = new XmlDocument();

            // XML Node
            XmlNode Xml_Node = null;

            string ReturnValue = "";

            try
            {
                Xml_Doc.LoadXml(XML);

                Xml_Node = Xml_Doc.SelectSingleNode(XPath);

                ReturnValue = Xml_Node.InnerText;

                return ReturnValue;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Parses a single node from a XML string
        /// </summary>
        /// <param name="XML">XML</param>
        /// <param name="XPath">Xpath</param>
        /// <returns>String value on success, throws exception on error</returns>
        public string ParseXMLFromString(string XML, string XPath, string Attribute)
        {
            // XML Document
            XmlDocument Xml_Doc = new XmlDocument();

            // XML Node
            XmlAttribute objXMLAttribute = null;

            string ReturnValue = "";

            try
            {
                Xml_Doc.LoadXml(XML);

                objXMLAttribute = Xml_Doc.SelectSingleNode(XPath).Attributes[Attribute];

                ReturnValue = objXMLAttribute.Value;

                return ReturnValue;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Single Node XML XPath Parser From File
        /// </summary>
        /// <param name="XML">XML String</param>
        /// <param name="XPath">XPath</param>
        /// <returns>String value on success, throws Exception on error</returns>
        public string ParseXMLFromFile(string AbsoluteFilePath, string XPath)
        {
            InputOutput _IO = new InputOutput();

            // XML Document
            XmlDocument Xml_Doc = new XmlDocument();

            // XML Node
            XmlNode Xml_Node = null;

            string ReturnXML = "", XML = "";
            bool ReadResult = false;

            try
            {
                XML = _IO.ReadFile(AbsoluteFilePath, out ReadResult);

                if (ReadResult == false)
                {
                    throw new Exception("Unable to read file");
                }

                Xml_Doc.LoadXml(XML);

                Xml_Node = Xml_Doc.SelectSingleNode(XPath);

                ReturnXML = Xml_Node.InnerText;

                return ReturnXML;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Single Node XML XPath Fast Parser From File
        /// </summary>
        /// <param name="XML">XML String</param>
        /// <param name="XPath">XPath</param>
        /// <returns>String value on success, throws Exception on error</returns>
        public string ParseFastXMLFromFile(string AbsoluteFilePath, string XPath)
        {
            InputOutput _IO = new InputOutput();

            // XML Document
            XmlDocument Xml_Doc = new XmlDocument();

            // XML Node
            XmlNode Xml_Node = null;

            string ReturnXML = "";

            try
            {

                Xml_Doc.Load(AbsoluteFilePath);

                Xml_Node = Xml_Doc.SelectSingleNode(XPath);

                ReturnXML = Xml_Node.InnerText;

                return ReturnXML;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Single Node XML XPath Fast Parser From File asynchronously
        /// </summary>
        /// <param name="XML">XML String</param>
        /// <param name="XPath">XPath</param>
        /// <returns>Task of string value on success, throws Exception on error</returns>
        public Task<string> ParseXMLFromFileAsync(string AbsoluteFilePath, string XPath)
        {
            Task<string> ProXMLTask = new Task<string>(() =>
                {
                    InputOutput _IO = new InputOutput();

                    // XML Document
                    XmlDocument Xml_Doc = new XmlDocument();

                    // XML Node
                    XmlNode Xml_Node = null;

                    string ReturnXML = "";

                    try
                    {

                        Xml_Doc.Load(AbsoluteFilePath);

                        Xml_Node = Xml_Doc.SelectSingleNode(XPath);

                        ReturnXML = Xml_Node.InnerText;

                        return ReturnXML;
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }
                });

            ProXMLTask.Start();

            return ProXMLTask;
        }

        /// <summary>
        /// Single Node XML XPath Fast Parser From File asynchronouly
        /// </summary>
        /// <param name="XML">XML String</param>
        /// <param name="XPath">XPath</param>
        /// <returns>Task of type string value on success, throws Exception on error</returns>
        public Task<string> ParseFastXMLFromFileAsync(string AbsoluteFilePath, string XPath)
        {
            Task<string> ProXMLTask = new Task<string>(() =>
            {
                InputOutput _IO = new InputOutput();

                // XML Document
                XmlDocument Xml_Doc = new XmlDocument();

                // XML Node
                XmlNode Xml_Node = null;

                string ReturnXML = "";

                try
                {

                    Xml_Doc.Load(AbsoluteFilePath);

                    Xml_Node = Xml_Doc.SelectSingleNode(XPath);

                    ReturnXML = Xml_Node.InnerText;

                    return ReturnXML;
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            });

            ProXMLTask.Start();

            return ProXMLTask;
        }

        /// <summary>
        /// Checks if the XML is well formed or not
        /// </summary>
        /// <param name="XML">XML as string</param>
        /// <param name="NotWellFormedException">Exception (out)</param>
        /// <returns>True if the XML is well formed, false if the XML is not well formed</returns>
        public bool IsWellFormedXML(string XML, out System.Exception NotWellFormedException)
        {
            bool WellFormedXML = false;

            try
            {
                XmlDocument xml_doc = new XmlDocument();
                xml_doc.LoadXml(XML);

                WellFormedXML = true;

                NotWellFormedException = null;

                return WellFormedXML;
            }
            catch (Exception ex)
            {
                WellFormedXML = false;

                NotWellFormedException = ex;

                return WellFormedXML;
            }
        }

        /// <summary>
        /// Loads an XML as string
        /// </summary>
        /// <param name="XML_AbsoluteFilePath">Absolute XML File Path (Full Path)</param>
        /// <returns>String as XML on success, throws exception on error</returns>
        public string LoadXMLAsString(string XML_AbsoluteFilePath)
        {
            // XML Document
            XmlDocument Xml_Doc = new XmlDocument();
            System.IO.StringWriter sw = new System.IO.StringWriter();
            System.IO.StringReader sr = null;
            string XMLAsString = "";

            try
            {
                Xml_Doc.Load(XML_AbsoluteFilePath);
                Xml_Doc.Save(sw);
                sr = new System.IO.StringReader(sw.ToString());
                XMLAsString = sr.ReadToEnd();

                return XMLAsString;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Loads an XML as string asynchronously
        /// </summary>
        /// <param name="XML_AbsoluteFilePath">Absolute XML File Path (Full Path)</param>
        /// <returns>Task of type string as XML on success, throws exception on error</returns>
        public Task<string> LoadXMLAsStringAsync(string XML_AbsoluteFilePath)
        {
            Task<string> ProXMLTask = new Task<string>(() =>
                {
                    // XML Document
                    XmlDocument Xml_Doc = new XmlDocument();
                    System.IO.StringWriter sw = new System.IO.StringWriter();
                    System.IO.StringReader sr = null;
                    string XMLAsString = "";

                    try
                    {
                        Xml_Doc.Load(XML_AbsoluteFilePath);
                        Xml_Doc.Save(sw);
                        sr = new System.IO.StringReader(sw.ToString());
                        XMLAsString = sr.ReadToEnd();

                        return XMLAsString;
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }
                });

            ProXMLTask.Start();

            return ProXMLTask;
        }

        /// <summary>
        /// Writes XML to file
        /// </summary>
        /// <param name="XML">XML to write</param>
        /// <param name="AbsoluteFilePath">Absolute File Path</param>
        /// <returns>True on success, throws exception on error</returns>
        public bool WriteXMLToFile(string XML, string AbsoluteFilePath)
        {
            try
            {
                XmlDocument objXMLDoc = new XmlDocument();
                objXMLDoc.LoadXml(XML);
                objXMLDoc.Save(AbsoluteFilePath);

                return true;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Writes XML to file asynchronously
        /// </summary>
        /// <param name="XML">XML to write</param>
        /// <param name="AbsoluteFilePath">Absolute File Path</param>
        /// <returns>Task of Type bool - True on success, throws exception on error</returns>
        public Task<bool> WriteXMLToFileAsync(string XML, string AbsoluteFilePath)
        {
            Task<bool> ProXMLTask = new Task<bool>(() =>
                {

                    try
                    {
                        XmlDocument objXMLDoc = new XmlDocument();
                        objXMLDoc.LoadXml(XML);
                        objXMLDoc.Save(AbsoluteFilePath);

                        return true;
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }
                });

            ProXMLTask.Start();

            return ProXMLTask;
        }

        /// <summary>
        /// Writes DataSet as XML to file
        /// </summary>
        /// <param name="ds">Data Set</param>
        /// <param name="AbsoluteFilePath">Absolute File Path</param>
        /// <returns>True on success, throws exception on error</returns>
        public bool WriteDataSetAsXMLToFile(DataSet ds, string AbsoluteFilePath)
        {
            try
            {
                string XML = ConvertDataSetToXML(ds);

                return WriteXMLToFile(XML, AbsoluteFilePath);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Writes DataSet as XML to file asynchronously
        /// </summary>
        /// <param name="ds">Data Set</param>
        /// <param name="AbsoluteFilePath">Absolute File Path</param>
        /// <returns>Task of Type bool - True on success, throws exception on error</returns>
        public Task<bool> WriteDataSetAsXMLToFileAsync(DataSet ds, string AbsoluteFilePath)
        {
            Task<bool> ProXMLTask = new Task<bool>(() =>
                {

                    try
                    {
                        string XML = ConvertDataSetToXML(ds);

                        return WriteXMLToFile(XML, AbsoluteFilePath);
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }
                });

            ProXMLTask.Start();

            return ProXMLTask;
        }

        /// <summary>
        /// Gets the Outer XML of a node
        /// </summary>
        /// <param name="XML">XML as string</param>
        /// <param name="XPath">XPath</param>
        /// <returns>Outer XML as string, throws exception on error</returns>
        public string GetNodeOuterXMLFromString(string XML, string XPath)
        {
            // XML Document
            XmlDocument Xml_Doc = new XmlDocument();

            // XML Node
            XmlNode Xml_Node = null;

            string ReturnValue = "";

            try
            {
                Xml_Doc.LoadXml(XML);

                Xml_Node = Xml_Doc.SelectSingleNode(XPath);

                ReturnValue = Xml_Node.OuterXml;

                return ReturnValue;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Gets node Inner XML
        /// </summary>
        /// <param name="XML">XML as string</param>
        /// <param name="XPath">XPath</param>
        /// <returns>Innser XML as string, throws exception on error</returns>
        public string GetNodeInnerXMLFromString(string XML, string XPath)
        {
            // XML Document
            XmlDocument Xml_Doc = new XmlDocument();

            // XML Node
            XmlNode Xml_Node = null;

            string ReturnValue = "";

            try
            {
                Xml_Doc.LoadXml(XML);

                Xml_Node = Xml_Doc.SelectSingleNode(XPath);

                ReturnValue = Xml_Node.InnerXml;

                return ReturnValue;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Gets all the nodes for the specified XPath
        /// </summary>
        /// <param name="XML">XML as string</param>
        /// <param name="XPath">XMPath</param>
        /// <returns>XML Node List on success, throws exception on error</returns>
        public XmlNodeList GetNodesFromXMLString(string XML, string XPath)
        {
            // XML Document
            XmlDocument Xml_Doc = new XmlDocument();

            // XML Node List
            XmlNodeList Xml_NodeList = null;

            try
            {
                Xml_Doc.LoadXml(XML);

                Xml_NodeList = Xml_Doc.SelectNodes(XPath);

                return Xml_NodeList;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// XML XPath Parser
        /// </summary>
        /// <param name="XML">XML as string</param>
        /// <param name="XPath">XPath</param>
        /// <returns>ArrayList on success, throws Exception on error</returns>
        public ArrayList ParseAllXMLFromString(string XML, string XPath)
        {
            ArrayList Data = new ArrayList();

            // XML Document
            XmlDocument Xml_Doc = new XmlDocument();

            // XML Node
            XmlNodeList Xml_Node = null;

            try
            {
                Xml_Doc.LoadXml(XML);

                Xml_Node = Xml_Doc.SelectNodes(XPath);

                // Make a Array List containg the values for the given XPath
                foreach (XmlElement element in Xml_Node)
                {
                    Data.Add(element.InnerText);
                }

                return Data;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// XML XPath Parser
        /// </summary>
        /// <param name="XML">XML as string</param>
        /// <param name="XPath">XPath</param>
        /// <returns>ArrayList on success, throws Exception on error</returns>
        public ArrayList ParseAllXMLFromFile(string AbsoluteFilePath, string XPath)
        {
            ArrayList Data = new ArrayList();
            InputOutput _IO = new InputOutput();

            // XML Document
            XmlDocument Xml_Doc = new XmlDocument();

            // XML Node
            XmlNodeList Xml_Node = null;
            try
            {
                string XML = "";

                bool ReadResult = false;

                XML = _IO.ReadFile(AbsoluteFilePath, out ReadResult);

                if (ReadResult == false)
                {
                    throw new Exception("Unable to read file");
                }

                Xml_Doc.LoadXml(XML);

                Xml_Node = Xml_Doc.SelectNodes(XPath);

                // Make a Array List containg the values for the given XPath
                foreach (XmlElement element in Xml_Node)
                {
                    Data.Add(element.InnerText);
                }

                return Data;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion
    }
}
